/*
 * Copyright(C) 2020. Huawei Technologies Co.,Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>

#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"

#include "ErrorCode/ErrorCode.h"
#include "VideoDecoderHost/VideoDecoder.h"
#include "Log/Log.h"
#include "FileEx/FileEx.h"
#include "CostStatistic/CostStatistic.h"
#include "TestCV/TestCV.h"
namespace ascendFaceRecognition {
namespace {
const uint32_t VPC_STRIDE_WIDTH = 16; // Vpc module output width need to align up to 16
const uint32_t VPC_STRIDE_HEIGHT = 2; // Vpc module output height need to align up to 2
const uint32_t YUV_BYTES_NU = 3;      // Numerator of yuv image, H x W x 3 / 2
const uint32_t YUV_BYTES_DE = 2;      // Denominator of yuv image, H x W x 3 / 2
const uint32_t ONE_THOUSAND_MILLISECOND = 1000;
const uint32_t TIME_OUT = 1000;
const uint32_t MAX_VIDEO_MEMORY_BLOCK_NUM = 14;
const uint32_t MAX_VDEC_WIDTH = 4096;
const uint32_t MAX_VDEC_HEIGHT = 4096;
const uint32_t MIN_VDEC_WIDTH = 128;
const uint32_t MIN_VDEC_HEIGHT = 128;
#ifdef ASCEND_FACE_USE_HI_MPI
const uint32_t REF_FRAME_NUM = 8;
const uint32_t DISPLAY_FRAME_NUM = 2;
const uint32_t SLEEP_TIME = 500;
const int MAX_HIMPI_CHN_NUM = 255;
#endif
}

bool VideoDecoder::stopDecoderThread = false;
#ifndef ASCEND_FACE_USE_HI_MPI
int VideoDecoder::decoderThreadSignal = 0;
pthread_t VideoDecoder::decoderThreadId = 0;
int VideoDecoder::stopCount = 0;
int VideoDecoder::instanceCount = 0;
int VideoDecoder::deviceStatus = -1;
#endif

VideoDecoder::VideoDecoder() {}

VideoDecoder::~VideoDecoder() {}

APP_ERROR VideoDecoder::ParseConfig(ConfigParser &configParser)
{
    std::string itemCfgStr = {};
    itemCfgStr = std::string("skipInterval");
    APP_ERROR ret = configParser.GetUnsignedIntValue(itemCfgStr, skipInterval_);
    if (ret != APP_ERR_OK) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: Fail to get config variable named " << itemCfgStr << ".";
        return ret;
    }

    itemCfgStr = std::string("streamWidthMax");
    ret = configParser.GetUnsignedIntValue(itemCfgStr, streamWidthMax_);
    if (ret != APP_ERR_OK) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: Fail to get config variable named " << itemCfgStr << ".";
        return ret;
    }

    itemCfgStr = std::string("streamHeightMax");
    ret = configParser.GetUnsignedIntValue(itemCfgStr, streamHeightMax_);
    if (ret != APP_ERR_OK) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: Fail to get config variable named " << itemCfgStr << ".";
        return ret;
    }

    itemCfgStr = std::string("stream.format");
    ret = configParser.GetStringValue(itemCfgStr, videoFormat_);
    if (ret != APP_ERR_OK) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: Fail to get config variable named " << itemCfgStr << ".";
        return ret;
    }
    if (videoFormat_ == "H264" || videoFormat_ == "H265") {
#ifdef ASCEND_FACE_USE_HI_MPI
        videoType_ = (videoFormat_ == "H264") ? HI_PT_H264 : HI_PT_H265;
#else
        videoType_ = (videoFormat_ == "H264") ? H264_MAIN_LEVEL : H265_MAIN_LEVEL;
#endif
    } else {
        LogError << "VideoDecoder stream format error, format = " << videoFormat_.c_str() << " not support";
        return APP_ERR_COMM_INNER;
    }
    if (streamWidthMax_ > MAX_VDEC_WIDTH || streamHeightMax_ > MAX_VDEC_HEIGHT || streamWidthMax_ < MIN_VDEC_WIDTH ||
        streamHeightMax_ < MIN_VDEC_HEIGHT) {
        LogError << "VideoDecoder[" << instanceId_
                 << "]: streamWidthMax or streamHeightMax is out of range [128, 4096]. " << "streamWidthMax: "
                 << streamWidthMax_ << ". streamHeightMax:" << streamHeightMax_ << ".";
        return APP_ERR_COMM_INNER;
    }
    return ret;
}

APP_ERROR VideoDecoder::Init(ConfigParser &configParser, ModuleInitArgs &initArgs)
{
    LogDebug << "Begin to init instance " << initArgs.instanceId;
#ifndef ASCEND_FACE_USE_HI_MPI
    instanceCount += 1;
#endif
    // init parameter
    AssignInitArgs(initArgs);
    int ret = ParseConfig(configParser);
    if (ret != APP_ERR_OK) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: Fail to parse config params." << GetAppErrCodeInfo(ret) <<
                 ".";
        return ret;
    }
    streamWidthMax_ = DVPP_ALIGN_UP(streamWidthMax_, VPC_STRIDE_WIDTH);
    streamHeightMax_ = DVPP_ALIGN_UP(streamHeightMax_, VPC_STRIDE_HEIGHT);
#ifndef ASCEND_FACE_USE_HI_MPI
    memPoolOrigin_ = MemoryPool::NewMemoryPoolResource();
    uint32_t blockSize = streamWidthMax_ * streamHeightMax_ * YUV_BYTES_NU / YUV_BYTES_DE;
    ret = memPoolOrigin_->Init(blockSize, MAX_VIDEO_MEMORY_BLOCK_NUM);
    if (ret != APP_ERR_OK) {
        return ret;
    }
#endif
    ret = InitVdec();
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: InitVdec failed, ret=" << ret << ".";
        return ret;
    }
    LogDebug << "VideoDecoder[" << instanceId_ << "]: VideoDecoder::Init OK.";

    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::DeInit(void)
{
    LogDebug << "VideoDecoder [" << instanceId_ << "] begin to deinit";

    APP_ERROR ret = DeInitVdec();
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: DeInit vdec fail. ret=" << ret;
    }
#ifndef ASCEND_FACE_USE_HI_MPI
    memPoolOrigin_->DeInit();
#endif
    LogDebug << "VideoDecoder [" << instanceId_ << "] deinit success.";
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::Process(std::shared_ptr<void> inputData)
{
    LogDebug << "VideoDecoder[" << instanceId_ << "]: VideoDecoder: process start.";
    std::shared_ptr<DataTrans> dataTrans = std::static_pointer_cast<DataTrans>(inputData);

    auto streamData = dataTrans->streamdata();
    // filter data
    if ((uint32_t)streamData.info().width() > streamWidthMax_ ||
        (uint32_t)streamData.info().height() > streamHeightMax_) {
        LogFatal << "VideoDecoder[" << instanceId_ << "]: VideoDecoder::channelId: " << streamData.info().channelid()
                 << ",frameId: " << streamData.info().frameid() << ",image size: width " << streamData.info().width()
                 << ", height " << streamData.info().height() << ". Too Big";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    auto startTime = CostStatistic::GetStart();
    std::shared_ptr<uint8_t> streamShared = AclrtMallocAndCopy(streamData);
    double costMs = CostStatistic::GetCostTime(startTime);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: VideoDecoder: AclrtMallocAndCopy " << costMs << "ms";
    std::shared_ptr<VdecInfo> vdecInfo = std::make_shared<VdecInfo>();
    vdecInfo->streamData = streamShared;
    vdecInfo->streamSize = streamData.datasize();
    vdecInfo->imageSize = streamWidthMax_ * streamHeightMax_ * YUV_BYTES_NU / YUV_BYTES_DE;
    if (frameId_ % skipInterval_ != 0) {
        vdecInfo->imageData = nullptr;
    } else {
#ifdef ASCEND_FACE_USE_HI_MPI
        uint8_t* blockBufferHeadMPI = nullptr;
        APP_ERROR ret = hi_mpi_dvpp_malloc(deviceId_, (void **)&blockBufferHeadMPI, vdecInfo->imageSize);
        if (ret != APP_ERR_OK) {
            LogError << "hi_mpi_dvpp_malloc: apply batch memory error ret=" << ret;
            blockBufferHeadMPI = nullptr;
            return ret;
        }
        vdecInfo->imageData.reset(blockBufferHeadMPI, hi_mpi_dvpp_free);
#else
        vdecInfo->imageData = memPoolOrigin_->GetMemoryBlock();
#endif
    }
    vdecInfo->frameId = frameId_;
    frameId_++;
    startTime = CostStatistic::GetStart();
    APP_ERROR ret = VideoDecode(vdecInfo);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: VideoDecode fail. ret=" << ret;
        return ret;
    }
    costMs = CostStatistic::GetCostTime(startTime);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: VideoDecoder: CombineVdecProcess " << costMs << "ms";
    LogDebug << "VideoDecoder[" << instanceId_ << "]: VideoDecoder: process end.";
    return APP_ERR_OK;
}

static void SetImageInfo(ImageInfo &imageInfo, std::shared_ptr<uint8_t> data, const uint32_t &width,
                         const uint32_t &height)
{
    uint32_t widthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH);
    uint32_t heightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT);
    imageInfo.buf.deviceData = data;
    imageInfo.buf.dataSize = widthStride * heightStride * YUV_BYTES_NU / YUV_BYTES_DE;
    imageInfo.width = width;
    imageInfo.height = height;
    imageInfo.widthAligned = widthStride;
    imageInfo.heightAligned = heightStride;
    imageInfo.format = 1;
}

std::shared_ptr<uint8_t> VideoDecoder::AclrtMallocAndCopy(StreamDataTrans streamData)
{
    auto startTime = CostStatistic::GetStart();
    void* streamBuffer = nullptr;
#ifdef ASCEND_FACE_USE_HI_MPI
    APP_ERROR ret = hi_mpi_dvpp_malloc(deviceId_, &streamBuffer, streamData.datasize());
    if (ret != APP_ERR_OK) {
        LogDebug << "Failed to hi_mpi_dvpp_malloc with " << streamData.datasize() << " bytes, ret = " << ret << ".";
        return nullptr;
    }
#else
    APP_ERROR ret = acldvppMalloc(&streamBuffer, streamData.datasize());
    if (ret != APP_ERR_OK) {
        LogError << "Failed to malloc data with " << streamData.datasize() << " bytes, ret = " << ret << ".";
        return nullptr;
    }
#endif
    double costMs = CostStatistic::GetCostTime(startTime);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: stream acldvppMalloc " << costMs << "ms";

    startTime = CostStatistic::GetStart();
    auto streamShared = std::make_shared<uint8_t>();
#ifdef ASCEND_FACE_USE_HI_MPI
    streamShared.reset(static_cast<uint8_t*>(streamBuffer), hi_mpi_dvpp_free);
#else
    streamShared.reset(static_cast<uint8_t *>(streamBuffer), acldvppFree);
#endif
    ret = aclrtMemcpy(streamShared.get(), streamData.datasize(), streamData.data().data(), streamData.datasize(),
                      ACL_MEMCPY_HOST_TO_DEVICE);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to copy memory";
        return nullptr;
    }
    costMs = CostStatistic::GetCostTime(startTime);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: stream aclrtMemcpy " << costMs << "ms";

    return streamShared;
}

#ifdef ASCEND_FACE_USE_HI_MPI

APP_ERROR VideoDecoder::CreateThread()
{
    LogDebug << "VideoDecoder pthread_create, init";
    int createThreadErr = pthread_create(&vdecThreadId_, nullptr, VideoDecoder::vdecThread, (void*) this);
    if (createThreadErr != 0) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to create thread710, err = " << createThreadErr;
        return APP_ERR_ACL_FAILURE;
    }

    return APP_ERR_OK;
}

void VideoDecoder::GetFrameAiInfoAndSendData(VideoDecoder* videoDecoder, hi_video_frame_info &frame)
{
    std::shared_ptr<FrameAiInfo> frameAiInfo = std::make_shared<FrameAiInfo>();
    frameAiInfo->info.channelId = videoDecoder->instanceId_;
    uint64_t frameId = frame.v_frame.pts;
    frameAiInfo->info.frameId = frameId;
    frameAiInfo->info.mode = FRAME_MODE_SEARCH;
    ImageInfo originImageInfo = {};
    std::shared_ptr<uint8_t> vdecOutBufferDev((uint8_t *)frame.v_frame.virt_addr[0], [](void* ptr) {});
    SetImageInfo(originImageInfo, vdecOutBufferDev, frame.v_frame.width_stride[0],
                 frame.v_frame.height_stride[0]);
    frameAiInfo->imgOrigin = originImageInfo;
    videoDecoder->SendToNextModule(MT_VIDEO_RESIZE, frameAiInfo, frameAiInfo->info.channelId);
}

void* VideoDecoder::vdecThread(void* arg)
{
    // Notice: Create context for this thread
    auto* videoDecoder = (VideoDecoder*) arg;
    if (videoDecoder == nullptr) {
        LogError << "arg is nullptr";
        return ((void*) (-1));
    }
    int instanceId = videoDecoder->instanceId_;
    aclError ret = aclrtSetCurrentContext(videoDecoder->aclContext_);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId << "]: Failed to set context, ret = " << ret;
        return ((void*) (-1));
    }
    LogInfo << "VideoDecoder[" << instanceId << "]: DecoderThread start";
    hi_video_frame_info frame;
    hi_vdec_stream stream;
    hi_vdec_supplement_info stSupplement {};
    while (!stopDecoderThread) {
        ret = hi_mpi_vdec_get_frame(videoDecoder->instanceId_, &frame, &stSupplement, &stream, TIME_OUT);
        if (ret != APP_ERR_OK) {
            LogDebug << "VideoDecoder[" << instanceId << "]:Failed to get frame with hi_mpi, ret = " << ret;
            usleep(SLEEP_TIME);
            continue;
        }
        int32_t decResult = frame.v_frame.frame_flag;
        if (decResult != APP_ERR_OK || stream.need_display == HI_FALSE || frame.v_frame.virt_addr[0] == nullptr) {
            ret = hi_mpi_vdec_release_frame(videoDecoder->instanceId_, &frame);
            if (ret != APP_ERR_OK) {
                LogError << "VideoDecoder[" << instanceId << "]: Failed to release_frame with hi_mpi, ret = " << ret;
                return ((void*) (-1));
            }
            continue;
        }
        GetFrameAiInfoAndSendData(videoDecoder, frame);
        ret = hi_mpi_vdec_release_frame(videoDecoder->instanceId_, &frame);
        if (ret != APP_ERR_OK) {
            LogError << "VideoDecoder[" << instanceId << "]: Failed to release_frame with hi_mpi, ret = " << ret;
            return ((void*) (-1));
        }
    }
    return nullptr;
}

void VideoDecoder::SetChannelAttribute(hi_vdec_chn_attr &chnAttr)
{
    // Configure channel attribute
    chnAttr.type = videoType_;
    chnAttr.mode = HI_VDEC_SEND_MODE_FRAME; // Only support frame mode
    chnAttr.pic_width = streamWidthMax_;
    chnAttr.pic_height = streamHeightMax_;
    // Stream buffer size, Recommended value is width * height * 3 / 2
    chnAttr.stream_buf_size = streamWidthMax_ * streamHeightMax_ * 3 / 2;
    chnAttr.frame_buf_cnt = REF_FRAME_NUM + DISPLAY_FRAME_NUM + 1;

    // create buf attribute
    hi_data_bit_width bitWidth = HI_DATA_BIT_WIDTH_8;
    hi_pic_buf_attr buf_attr {
        streamWidthMax_, streamHeightMax_, 0, bitWidth, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420, HI_COMPRESS_MODE_NONE
    };
    chnAttr.frame_buf_size = hi_vdec_get_pic_buf_size(chnAttr.type, &buf_attr);

    // Configure video decoder channel attribute
    chnAttr.video_attr.ref_frame_num = REF_FRAME_NUM;
    chnAttr.video_attr.temporal_mvp_en = HI_TRUE;
    chnAttr.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(chnAttr.type, streamWidthMax_, streamHeightMax_);
}

APP_ERROR VideoDecoder::ReSetChannelParm(hi_vdec_chn_param &chnParam)
{
    // Get channel parameter
    APP_ERROR ret = hi_mpi_vdec_get_chn_param(instanceId_, &chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to get vdec channel param with hi_mpi.";
        return APP_ERR_ACL_FAILURE;
    }
    chnParam.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB;
    chnParam.video_param.compress_mode = HI_COMPRESS_MODE_HFBC;
    chnParam.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16;
    chnParam.display_frame_num = DISPLAY_FRAME_NUM;
    chnParam.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY;

    // Set channel parameter
    ret = hi_mpi_vdec_set_chn_param(instanceId_, &chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to set vdec channel param with hi_mpi.";
        return APP_ERR_ACL_FAILURE;
    }
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::CreateVdecChn(hi_vdec_chn_attr &chnAttr, int &channelId)
{
    LogDebug << "Begin CreateVdecChn";
    APP_ERROR ret;
    channelId = 0;
    while (channelId <= MAX_HIMPI_CHN_NUM) {
        LogDebug << "now trying to create chn, chnid is " << channelId;
        ret = hi_mpi_vdec_create_chn(channelId, &chnAttr);
        if (ret == APP_ERR_OK) {
            LogInfo << "Create vdec channel success. channel id is " << channelId << ".";
            break;
        } else if (channelId == MAX_HIMPI_CHN_NUM) {
            LogError << "Failed to create vdec channel, All channels are occupied.";
            return ret;
        } else if (ret == (int)HI_ERR_VDEC_EXIST) {
            channelId++;
        } else {
            return ret;
        }
    }
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::InitVdec()
{
    APP_ERROR ret = hi_mpi_sys_init();
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to init sys with hi_mpi.";
        return APP_ERR_ACL_FAILURE;
    }
    // create channel attribute
    hi_vdec_chn_attr chnAttr[1] {};
    SetChannelAttribute(chnAttr[0]);
    ret = hi_mpi_vdec_create_chn(instanceId_, &chnAttr[0]);
    if (ret != APP_ERR_OK) {
        ret = CreateVdecChn(chnAttr[0], instanceId_);
        if (ret != APP_ERR_OK) {
            LogError << "VideoDecoder[" << instanceId_ << "]: Failed to create vdec channel with hi_mpi.";
            return ret;
        }
    }
    hi_vdec_chn_param chnParam;
    ret = ReSetChannelParm(chnParam);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to reset vdec channel parm with hi_mpi.";
        return APP_ERR_ACL_FAILURE;
    }
    // Decoder channel start receive stream
    ret = hi_mpi_vdec_start_recv_stream(instanceId_);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to start receiving vdec stream.";
        return APP_ERR_ACL_FAILURE;
    }
    ret = CreateThread();
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to create thread, err = " << ret;
        return APP_ERR_ACL_FAILURE;
    }
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::DeInitVdec()
{
    stopDecoderThread = true;
    pthread_join(vdecThreadId_, NULL);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: DeInitVdec OK ";

    APP_ERROR ret = hi_mpi_vdec_stop_recv_stream(instanceId_);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to stop receiving stream with hi_mpi. ret=" << ret;
        return ret;
    }

    ret = hi_mpi_vdec_destroy_chn(instanceId_);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to destroy vdec channel with hi_mpi. ret=" << ret;
        return ret;
    }

    ret = hi_mpi_sys_exit();
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to exit sys with hi_mpi. ret=" << ret;
        return ret;
    }

    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::VideoDecode(std::shared_ptr<VdecInfo> &vdecInfo)
{
    // create stream
    hi_vdec_stream stream;
    stream.pts = vdecInfo->frameId;
    stream.addr = vdecInfo->streamData.get();
    stream.len = vdecInfo->streamSize;
    stream.end_of_frame = HI_TRUE;
    stream.end_of_stream = HI_FALSE;
    // create output picture info
    hi_vdec_pic_info outPicInfo;
    outPicInfo.width = 0;
    outPicInfo.height = 0;
    outPicInfo.width_stride = streamWidthMax_;
    outPicInfo.height_stride = streamHeightMax_;
    outPicInfo.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    // set skip frame parm
    if (vdecInfo->frameId % skipInterval_ != 0) {
        stream.need_display = HI_FALSE;
        outPicInfo.vir_addr = 0;
        outPicInfo.buffer_size = 0;
    } else {
        stream.need_display = HI_TRUE;
        outPicInfo.vir_addr = (uint64_t) vdecInfo->imageData.get();
        outPicInfo.buffer_size = vdecInfo->imageSize;
    }

    APP_ERROR ret = hi_mpi_vdec_send_stream(instanceId_, &stream, &outPicInfo, TIME_OUT);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: Failed to send vdec stream with hi_mpi. ret=" << ret;
        return ret;
    }
    return APP_ERR_OK;
}

#else

APP_ERROR VideoDecoder::InitVdec()
{
    APP_ERROR createThreadErr = VideoDecoder::CreateThread();
    if (createThreadErr != APP_ERR_OK) {
        LogError << "Failed to create thread, err = " << createThreadErr;
        return APP_ERR_ACL_FAILURE;
    }
    aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc();
    if (vdecChannelDesc == nullptr) {
        LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecCreateChannelDesc fail.";
        return APP_ERR_ACL_FAILURE;
    }
    vdecChannelDesc_.reset(vdecChannelDesc, aclvdecDestroyChannelDesc);
    APP_ERROR ret1 = aclvdecSetChannelDescChannelId(vdecChannelDesc, instanceId_);
    APP_ERROR ret2 = aclvdecSetChannelDescThreadId(vdecChannelDesc, decoderThreadId);
    APP_ERROR ret3 = aclvdecSetChannelDescCallback(vdecChannelDesc, &VideoDecoder::VideoDecoderCallback);
    APP_ERROR ret4 = aclvdecSetChannelDescEnType(vdecChannelDesc, videoType_);
    APP_ERROR ret5 = aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    APP_ERROR ret6 = aclvdecSetChannelDescOutMode(vdecChannelDesc, 0);
    APP_ERROR ret7 = aclvdecSetChannelDescOutPicWidth(vdecChannelDesc, streamWidthMax_);
    APP_ERROR ret8 = aclvdecSetChannelDescOutPicHeight(vdecChannelDesc, streamHeightMax_);
    APP_ERROR ret9 = aclvdecCreateChannel(vdecChannelDesc);
    if (ret1 != APP_ERR_OK || ret2 != APP_ERR_OK || ret3 != APP_ERR_OK ||
        ret4 != APP_ERR_OK || ret5 != APP_ERR_OK || ret6 != APP_ERR_OK ||
        ret7 != APP_ERR_OK || ret8 != APP_ERR_OK || ret9 != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecSet fail" <<
                 ", ret1 = " << ret1 << ", ret2 = " << ret2 << ", ret3 = " << ret3 <<
                 ", ret4 = " << ret4 << ", ret5 = " << ret5 << ", ret6 = " << ret6 <<
                 ", ret7 = " << ret7 << ", ret8 = " << ret8 << ", ret9 = " << ret9;
        return APP_ERR_ACL_FAILURE;
    }
    VideoDecoder::deviceStatus += 1;
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::DeInitVdec()
{
    APP_ERROR ret = aclvdecDestroyChannel(vdecChannelDesc_.get());
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecDestroyChannel fail. ret=" << ret;
        return ret;
    }
    vdecChannelDesc_.reset();
    if (stopCount == (instanceCount - 1)) {
        stopDecoderThread = true;
        pthread_join(decoderThreadId, NULL);
    }
    stopCount += 1;
    LogDebug << "VideoDecoder[" << instanceId_ << "]: DeInitVdec OK ";
    return APP_ERR_OK;
}

void *VideoDecoder::DecoderThread(void *arg)
{
    // Notice: Create context for this thread
    VideoDecoder *videoDecoder = (VideoDecoder *)arg;
    if (videoDecoder == nullptr) {
        LogError << "arg is nullptr";
        return ((void *)(-1));
    }

    aclError ret = aclrtSetCurrentContext(videoDecoder->aclContext_);
    if (ret != APP_ERR_OK) {
        LogError << "Failed to set context, ret = " << ret;
        return ((void *)(-1));
    }

    LogInfo << "DecoderThread start";
    while (!stopDecoderThread) {
        LogDebug << "VideoDecoder[" << videoDecoder->instanceId_ << "] DecoderThread report ";
        (void)aclrtProcessReport(ONE_THOUSAND_MILLISECOND);
    }
    return nullptr;
}

void VideoDecoder::VideoDecoderCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userData)
{
    APP_ERROR ret = acldvppGetPicDescRetCode(output);
    std::shared_ptr<acldvppStreamDesc> inputDecs(input, acldvppDestroyStreamDesc);
    std::shared_ptr<acldvppPicDesc> outputDecs(output, acldvppDestroyPicDesc);

    DecodeH26xInfo *decodeH26xInfo = (DecodeH26xInfo *)userData;
    if (decodeH26xInfo == nullptr) {
        LogError << "VideoDecoder: user data is nullptr";
        return;
    }
    auto decodeH26xInfoPtr = std::make_shared<DecodeH26xInfo>();
    decodeH26xInfoPtr.reset(decodeH26xInfo, [](DecodeH26xInfo *p) {
        delete p;
        p = nullptr;
    });
    VideoDecoder *decoderPtr = decodeH26xInfo->videoDecoder;
    if (decoderPtr == nullptr) {
        LogError << "VideoDecoder: pointer is nullptr";
        return;
    }

    if (ret != APP_ERR_OK) {
        LogError << "vdec error ret=" << ret;
        return;
    }

    if ((decodeH26xInfoPtr->vdecInfo->frameId % decoderPtr->skipInterval_) != 0) {
        return;
    }

    std::shared_ptr<FrameAiInfo> frameAiInfo = std::make_shared<FrameAiInfo>();
    frameAiInfo->info.channelId = decoderPtr->instanceId_;
    frameAiInfo->info.frameId = decodeH26xInfoPtr->vdecInfo->frameId;
    frameAiInfo->info.mode = FRAME_MODE_SEARCH;
    ImageInfo originImageInfo = {};
    SetImageInfo(originImageInfo, decodeH26xInfo->vdecInfo->imageData, decoderPtr->streamWidthMax_,
                 decoderPtr->streamHeightMax_);
    frameAiInfo->imgOrigin = originImageInfo;
    decoderPtr->SendToNextModule(MT_VIDEO_RESIZE, frameAiInfo, frameAiInfo->info.channelId);
    return;
}

APP_ERROR VideoDecoder::CreateThread()
{
    if (decoderThreadSignal == 0) {
        LogDebug << "VideoDecoder pthread_create, init";
        int createThreadErr = pthread_create(&decoderThreadId, nullptr, VideoDecoder::DecoderThread, (void *)this);
        if (createThreadErr != 0) {
            LogError << "Failed to create thread, err = " << createThreadErr;
            return APP_ERR_ACL_FAILURE;
        }
        decoderThreadSignal += 1;
    }
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::SendFrameAsync(std::shared_ptr<VdecInfo> vdecInfo, acldvppStreamDesc *dvppStreamDesc,
                                       void *HInfo)
{
    DecodeH26xInfo *decoderH26xInfo = (DecodeH26xInfo *)HInfo;
    auto startTime = CostStatistic::GetStart();
    if (vdecInfo->frameId % skipInterval_ != 0) {
        APP_ERROR ret1 = aclvdecSendSkippedFrame(vdecChannelDesc_.get(), dvppStreamDesc, nullptr,
            static_cast<void *>(decoderH26xInfo));
        if (ret1 != APP_ERR_OK) {
            LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecSendSkipped Frame fail. ret=" << ret1;
            return ret1;
        }
        auto costMs = CostStatistic::GetCostTime(startTime);
        LogDebug << "VideoDecoder[" << instanceId_ << "]: aclvdecSendSkippedFrame " << costMs << "ms";
        return APP_ERR_OK;
    }
    acldvppPicDesc *dvppPicDesc = acldvppCreatePicDesc();
    if (dvppPicDesc == nullptr) {
        LogError << "VideoDecoder[" << instanceId_ << "]: acldvppCreatePicDesc";
        return APP_ERR_ACL_FAILURE;
    }
    APP_ERROR ret2 = acldvppSetPicDescData(dvppPicDesc, vdecInfo->imageData.get());
    APP_ERROR ret3 = acldvppSetPicDescSize(dvppPicDesc, vdecInfo->imageSize);
    if (ret2 != APP_ERR_OK || ret3 != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: acldvppSetPicDesc";
        acldvppDestroyPicDesc(dvppPicDesc);
        return APP_ERR_ACL_FAILURE;
    }
    APP_ERROR ret4 = aclvdecSendFrame(vdecChannelDesc_.get(), dvppStreamDesc, dvppPicDesc, nullptr,
        static_cast<void *>(decoderH26xInfo));
    if (ret4 != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecSendFrame fail. ret=" << ret4;
        acldvppDestroyPicDesc(dvppPicDesc);
        return ret4;
    }
    auto costMs = CostStatistic::GetCostTime(startTime);
    LogDebug << "VideoDecoder[" << instanceId_ << "]: aclvdecSendFrame " << costMs << "ms";
    return APP_ERR_OK;
}

APP_ERROR VideoDecoder::VideoDecode(std::shared_ptr<VdecInfo> &vdecInfo)
{
    acldvppStreamDesc *dvppStreamDesc = acldvppCreateStreamDesc();
    if (dvppStreamDesc == nullptr) {
        LogError << "VideoDecoder[" << instanceId_ << "]: acldvppCreateStreamDesc";
        return APP_ERR_ACL_FAILURE;
    }
    APP_ERROR ret1 = acldvppSetStreamDescData(dvppStreamDesc, vdecInfo->streamData.get());
    APP_ERROR ret2 = acldvppSetStreamDescSize(dvppStreamDesc, vdecInfo->streamSize);
    APP_ERROR ret3 = acldvppSetStreamDescTimestamp(dvppStreamDesc, vdecInfo->frameId);
    if (ret1 != APP_ERR_OK || ret2 != APP_ERR_OK || ret3 != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: acldvppSetStreamDesc fail.";
        acldvppDestroyStreamDesc(dvppStreamDesc);
        return APP_ERR_ACL_FAILURE;
    }
    DecodeH26xInfo *decoderH26xInfo = new DecodeH26xInfo();
    decoderH26xInfo->videoDecoder = this;
    decoderH26xInfo->vdecInfo = vdecInfo;
    APP_ERROR ret = SendFrameAsync(vdecInfo, dvppStreamDesc, decoderH26xInfo);
    if (ret != APP_ERR_OK) {
        LogError << "VideoDecoder[" << instanceId_ << "]: aclvdecSendFrame fail. ret=" << ret;
        delete decoderH26xInfo;
        decoderH26xInfo = nullptr;
        acldvppDestroyStreamDesc(dvppStreamDesc);
        return ret;
    }
    return APP_ERR_OK;
}

#endif
} // namespace ascendFaceRecognition